<template>
  <div class="list-container" @mousewheel="handleScroll">
    <div
      v-for="chart in recommendedCharts"
      :key="chart.tempId"
      class="list-item"
    >
      <Chart
        class="chart"
        :chart-type="chart.chartType"
        :config="chart.chartOptions"
      />
      <!-- <div class="button" title="添加可视化收藏">
        <a-icon-font class="icon" type="iconjiahaowhite" @click="onSave(chart)"/>
      </div> -->
      <div class="button" title="添加可视化收藏" @click="onSave(chart)">
        <a-icon-font class="icon" type="iconzengjia" style="color: #6973ff;" />
        添加收藏
      </div>
      <div class="mask" title="点击放大" @click.stop="showModal(chart)"></div>
    </div>
    <div
      v-if="isLoading"
      class="spinner-mask"
      :class="{ 'list-item': recommendedCharts.length }"
    >
      <a-spin class="spinner" :spinning="isLoading" />
    </div>
    <div
      v-show="
        !hasSelectedNode || (!isLoading && recommendedCharts.length === 0)
      "
      class="empty-status-container"
    >
      <img class="empty-img" :src="publicPath + '/img/empty.svg'" />
      暂无可视化推荐
    </div>
    <visualization-preview-modal
      :visible="isModalVisible"
      :title="selectNode ? selectNode.name : ''"
      :chart="modalChart"
      @close="isModalVisible = false"
      @save-chart="onSave(modalChart)"
    />
    <!-- <visualization-edit-modal
      :is-modal-visible="editChartModalVisible"
      @ok="onConfirmEdit"
      @cancel="editChartModalVisible = false"
    /> -->
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import Chart from '@/components/chart/chart.vue'
import DataStore from '@/store/modules/dataview'
import {
  getAndDealWidgetData,
  getRecommendedChart,
  getRecommendParameters,
  RecommendParameters,
  removeNullEntries,
} from '@/util/recommend-visualization'
import { cloneDeep, debounce, isEqual, throttle } from 'lodash'
import { saveWidget } from '@/api/widget'
import VisualizationPreviewModal from '@/components/studio/data/node-edit-panel/VisualizationPreviewModal.vue'
import { WidgetData } from '@/util/chart-widget'
import { chartTypes } from '@/config/contant'
import VisualizationEditModal from '@/components/studio/data/node-edit-panel/VisualizationEditModal.vue'
import { KeyValueStore } from '@/components/vueGridLayout/interfate/grid-Item-data-inteface'
import axios from 'axios'
import { publicPath } from '@/api/constants'

const { CancelToken } = axios // axios取消请求token
let axiosSource = CancelToken.source()

@Component({
  components: {
    Chart,
    VisualizationPreviewModal,
    VisualizationEditModal,
  },
})
export default class VisualizationRecommend extends Vue {
  private chartSize = [428, 284]
  private miscChartOptions = {
    size: [428, 284],
    labelIsShow: false,
    legendIsShow: false,
    padding: [20, 20, 20, 20],
    titleIsShow: false,
  }
  private heatMapOptions = {
    color: [
      '#5760e6',
      '#FBC94B',
      '#65DCB8',
      '#EF7D52',
      '#F596C6',
      '#687B9D',
      '#5EC6D7',
      '#FFA65E',
    ],
    legendBasicColor: '#5760e6',
  }

  private recommendedCharts: any[] = []
  private chartParameters: RecommendParameters[] = []
  private isLoading: boolean = false
  private isTableChanged: boolean = false
  private isModalVisible: boolean = false
  private modalChart: any = null
  private recommendCalledCount = 0
  private editChartModalVisible: boolean = false
  private publicPath = publicPath

  // 是否已选中某个节点
  private get hasSelectedNode() {
    return !!DataStore.selectNode
  }

  private get selectedColumns() {
    return DataStore.selectedColumns
  }

  private get selectNode() {
    return DataStore.selectNode
  }

  private get currentHistory() {
    return DataStore.currentHistory
  }

  private get tableColumns() {
    return DataStore.tableColumns
  }

  private get tableName(): string {
    // 查询数据的表名取决于当前历史记录的表名，没有的话取selectnode的表名
    return this.selectNode
      ? this.currentHistory?.data?.table ??
          this.selectNode?.data?.output?.[0]?.tableName
      : ''
  }

  private get fullTableName(): string {
    let table = this.tableName
    if (table?.endsWith?.('_')) {
      table += this.selectNode?.data?.lastTimeStamp ?? ''
    }
    if (table && !table.includes('.')) {
      table = `dataset.${table}`
    }
    return table
  }

  mounted() {
    if (this.tableColumns.length > 0 || this.selectedColumns.length > 0) {
      this.isLoading = true
      this.getRecommendedCharts()
    }
  }

  @Watch('fullTableName')
  private onFullTableNameChange() {
    this.isTableChanged = true
    this.recommendedCharts = []
  }

  @Watch('tableColumns')
  private onTableColumnsChange() {
    if (this.isTableChanged && this.tableColumns.length > 0) {
      this.isTableChanged = false
      this.isLoading = true
      this.getRecommendedCharts()
    } else if (this.tableColumns.length === 0) {
      this.recommendedCharts = []
    }
  }

  @Watch('selectedColumns')
  private onSelectedColumnsChange(newValue: any, oldValue: any) {
    if (!isEqual(newValue, oldValue) && this.selectNode) {
      this.recommendedCharts = []
      this.isLoading = true
      this.getRecommendedCharts()
    }
  }

  @Watch('recommendedCharts')
  private onIsLoadingChange() {
    const listHeight = this.recommendedCharts.reduce((height, chart) => {
      return height + chart.chartOptions.size[1]
    }, 0)
    this.$emit('charts-loaded', { listHeight })
  }

  private getRecommendedCharts = debounce(
    () => this._getRecommendedCharts(),
    600
  )

  private async _getRecommendedCharts() {
    this.recommendCalledCount += 1
    const { recommendCalledCount } = this
    this.chartParameters = []
    this.recommendedCharts = []
    this.isLoading = true
    if (this.tableColumns.length > 0) {
      this.chartParameters = getRecommendParameters(
        this.selectedColumns,
        this.tableColumns
      )
      if (axiosSource) {
        axiosSource.cancel('cancel request')
      }
      axiosSource = CancelToken.source()
      ;(async (count) => {
        // 借助 count 来记录新旧请求
        const charts = await this.getMoreCharts(axiosSource.token)
        this.isLoading = false
        if (charts.length > 0) {
          if (count === this.recommendCalledCount) {
            this.recommendedCharts = this.recommendedCharts.concat(charts)
          } else {
            // 不匹配直接丢弃
            this.recommendedCharts = []
          }
          this.recommendedCharts.forEach((chart: any) => {
            chart.chartOptions.size = this.chartSize
          })
        }
      })(recommendCalledCount)
    }
  }

  private async getMoreCharts(cancelToken?: any) {
    const loadCount = 3
    const parameters = this.chartParameters.slice(0, loadCount)
    this.chartParameters = this.chartParameters.slice(loadCount)
    const promises = parameters.map(async (parameter: any) => {
      const widgetData: WidgetData = await getRecommendedChart(
        parameter,
        this.fullTableName,
        this.selectNode.id, // this.currentHistory?.data?.id ?? this.selectNode.id,
        'task', // this.selectNode.type === 5 ? 'tclean' : 'task',
        this.selectNode.id,
        cancelToken
      )
      // if (widgetData) {
      //   const data = await this.getChartDataByWidget(widgetData)
      //   if (data.length === 0) {
      //     return null
      //   }
      //   widgetData.chartOptions.value = data

      // if (
      //   (parameter?.transformSetting &&
      //     parameter.chartSetting.chartType !== ChartDetailsEnum.pieChart) ||
      //   disableCleanActionChart.includes(parameter.chartSetting.chartType)
      // ) {
      //   // 为可视化清洗添加的配置
      //   widgetData.chartOptions.cleanActions = []
      // } else {
      //   widgetData.chartOptions.cleanActions = ['brush', 'legend']
      // }
      // }

      return widgetData
    })
    const charts = (await Promise.all(promises))
      .filter((chart) => chart !== null)
      .map((chart) => {
        chart!.chartOptions = {
          ...chart!.chartOptions,
          ...this.miscChartOptions,
          ...this.getExtraChartOptions(chart!),
        }
        if (
          chart.chartType === 'heatmapMatrix' ||
          chart.chartType[0] === 'heatmapMatrix'
        ) {
          chart!.chartOptions = {
            ...chart!.chartOptions,
            ...this.heatMapOptions,
          }
        }
        //
        return chart
      })

    // TODO apply bindChartData
    return charts
  }

  private get projectId() {
    return (this.$route.params as any).id
  }

  private onSave(chart: any) {
    const widgetData = this.getWidgetConfig(chart)
    DataStore.saveRecommendVisualization(widgetData)
  }

  private onConfirmEdit(widgetData: WidgetData) {
    DataStore.saveRecommendVisualization(widgetData)
    this.editChartModalVisible = false
  }

  private async saveWidget(widgetData: WidgetData) {
    const response = await saveWidget({
      data: {
        data: widgetData,
        name:
          chartTypes[
            Array.isArray(widgetData.chartType)
              ? widgetData.chartType[0]
              : widgetData.chartType
          ].title,
        randomId: Math.floor(Math.random() * 10000),
        tid: this.selectNode.id,
        type: 'task',
        projectId: this.projectId,
      },
    })
    return response.data // {result: widgetId}
  }

  private getWidgetConfig(chart: any) {
    const { tempId, showChart, ...data } = cloneDeep({
      ...chart,
      chartOptions: { ...chart.chartOptions },
    })
    return data
  }

  private async getChartDataByWidget(widgetData: WidgetData) {
    const data = await getAndDealWidgetData(widgetData)
    // TODO: temp: remove null entries to avoid rendering error
    return removeNullEntries(<any[]>data)
  }

  private showModal(chart: any) {
    this.isModalVisible = true
    this.modalChart = chart
  }

  private getExtraChartOptions(chart: WidgetData) {
    const result: any = {}
    const { value } = chart.chartOptions

    // hack for 异常检测散点图配色
    if ([1, -1, '1', '-1'].includes(value?.[0]?.label)) {
      const abnormalIndex = value.findIndex((v: any) =>
        [-1, '-1'].includes(v.label)
      )
      if (abnormalIndex > 0) {
        result.value = [
          value[abnormalIndex],
          ...value.slice(0, abnormalIndex),
          ...value.slice(abnormalIndex + 1),
        ]
      }
    }
    // end of hack

    if (
      chart.chartType === 'scatterplot' ||
      chart.chartType[0] === 'scatterplot'
    ) {
      result.size = [300, 300]
      result.padding = [10, 10, 10, 10]
    }
    const { algName } = this.selectNode?.data
    switch (algName) {
      case 'STAT_ANOMALY':
        this.parseSTATANOMALYOptions(result, chart.chartOptions)
        break
      default:
        break
    }

    return result
  }

  private parseSTATANOMALYOptions(result: any, originOptions: KeyValueStore) {
    const { valueKey, value = [] } = originOptions
    if (value.length === 0) {
      return
    }
    const normalValue = value.filter((v: any) => +v.label === 1) // 正常节点
    let min = Number.MAX_VALUE
    let max = Number.MIN_VALUE
    normalValue.forEach((data: any) => {
      min = Math.min(data[valueKey], min)
      max = Math.max(data[valueKey], max)
    })
    result.mode = 'percent'
    result.normalDataRange = [min, max]
    result.xAxisTitle = valueKey
  }

  private handleScroll = throttle(() => this._handleScroll(), 300)

  private async _handleScroll() {
    if (this.chartParameters.length > 0) {
      const { scrollHeight, scrollTop, offsetHeight } = this.$el as HTMLElement
      if (scrollHeight - scrollTop - offsetHeight < 300 && !this.isLoading) {
        this.isLoading = true
        const charts = await this.getMoreCharts()
        if (charts.length > 0) {
          this.recommendedCharts = this.recommendedCharts.concat(charts)
          this.recommendedCharts.forEach((chart: any) => {
            chart.chartOptions.size = this.chartSize
          })
        }
        this.isLoading = false
      }
    }
  }
}
</script>

<style lang="less" scoped>
.list-container {
  background: #fff;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto;
  width: 428px;

  .list-item {
    border: 1px solid #fff;
    border-radius: 4px;
    flex: 0 0 284px;
    height: 284px;
    margin: 0 0 20px;
    min-height: 0;
    // padding: 10px;
    position: relative;
    width: 428px;

    .button {
      background: #eff0fe;
      border: 1px solid #d3daff;
      border-radius: 4px;
      color: #6973ff;
      cursor: pointer;
      font-size: 14px;
      font-weight: 400;
      height: 52px;
      left: -1px;
      letter-spacing: 1px;
      line-height: 52px;
      opacity: 0.9;
      position: absolute;
      text-align: center;
      top: calc(100% - 25px);
      transform: translateY(-50%);
      visibility: hidden;
      width: calc(100% + 2px);
      z-index: 301;

      .icon {
        color: #6973ff;
        font-size: 14px;
      }
    }

    .mask {
      background: transparent;
      cursor: pointer;
      height: 100%;
      left: 0;
      position: absolute;
      top: 0;
      width: calc(100% - 16px);
      z-index: 300;
    }

    &:hover .button {
      visibility: visible;
    }

    // &:first-of-type {
    //   margin-top: 10px;
    // }

    // &:last-of-type {
    //   margin-bottom: 10px;
    // }

    &.more {
      cursor: pointer;
      flex: 0 0 164px;
      line-height: 164px;
      text-align: center;
    }

    &.square {
      flex: 0 0 300px;
      margin: 0 14px;
    }
  }

  .list-item:hover {
    border: 1px solid #d3daff;
    box-shadow: 0 0 4px 0 rgba(88, 98, 230, 0.2);
  }

  .empty-status-container {
    align-items: center;
    display: flex;
    flex-direction: column;
    height: 100%;
    justify-content: center;

    .empty-img {
      margin-bottom: 15px;
      margin-top: 200px;
    }
  }

  .spinner-mask {
    align-items: center;
    display: flex;
    height: 100%;
    justify-content: center;
    z-index: 1002;
  }

  &::after {
    content: '';
    padding-right: 9px;
  }

  &::-webkit-scrollbar {
    width: 0 !important;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(144, 147, 153, 0.5);
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 5px;
  }
}
</style>
